package cn.hg.solon.youcan.system.provider;

import static cn.hg.solon.youcan.system.entity.table.SysPermissionTableDef.SYS_PERMISSION;
import static cn.hg.solon.youcan.system.entity.table.SysRolePermissionMappingTableDef.SYS_ROLE_PERMISSION_MAPPING;
import static cn.hg.solon.youcan.system.entity.table.SysUserRoleMappingTableDef.SYS_USER_ROLE_MAPPING;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.util.ObjUtil;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Cache;

import com.mybatisflex.core.query.FindInSetQueryCondition;
import com.mybatisflex.core.query.FunctionQueryColumn;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryOrderBy;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.solon.service.impl.ServiceImpl;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.CacheConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.system.entity.SysPermission;
import cn.hg.solon.youcan.system.entity.SysRole;
import cn.hg.solon.youcan.system.entity.SysUser;
import cn.hg.solon.youcan.system.mapper.SysPermissionMapper;
import cn.hg.solon.youcan.system.mapper.SysRolePermissionMappingMapper;
import cn.hg.solon.youcan.system.service.SysPermissionService;

/**
 * @author 胡高
 */
@Component
public class SysPermissionProvider extends ServiceImpl<SysPermissionMapper, SysPermission>
implements SysPermissionService {

    @Inject
    SysRolePermissionMappingMapper rolePermissionMappingMapper;
    
    /**
     * <pre>
     * SELECT p.*
     * FROM sys_permission AS p
     * WHERE #if(status)p.status = #para(status)#else p.status <> 'DEL'#end
     *     #if(type)AND p.`type`=#para(type)#end
     *     #if(word)AND (p.`name` #likePara(word) OR  p.`value` #likePara(word))#end
     *     #if(word || type)OR FIND_IN_SET(p.id, (
     *         SELECT GROUP_CONCAT(p2.ancestors)
     *         FROM sys_permission AS p2
     *         WHERE #if(status)p2.status = #para(status)#else p2.status <> 'DEL'#end
     *             #if(type)AND p2.`type`=#para(type)#end
     *             #if(word)AND (p2.`name` #likePara(word) OR  p2.`value` #likePara(word))#end
     *     ))#end
     * </pre>
     */
    private QueryWrapper buildQuery(Map<String, Object> paraMap) {
        String word = (String) paraMap.get("word");
        String type = (String) paraMap.get("type");
        String status = (String) paraMap.get("status");
        QueryWrapper query = QueryWrapper.create()
            .from(SYS_PERMISSION)
            .where(SYS_PERMISSION.STATUS.eq(status).when(StrValidator.isNotEmpty(status))
                .and(SYS_PERMISSION.TYPE.eq(type).when(StrValidator.isNotEmpty(type)))
                .and(SYS_PERMISSION.NAME.like(word).when(StrValidator.isNotEmpty(word))
                    .or(SYS_PERMISSION.VALUE.like(word).when(StrValidator.isNotEmpty(word))))
                .or(new FindInSetQueryCondition(SYS_PERMISSION.ID, QueryWrapper.create()
                    .select(new FunctionQueryColumn("GROUP_CONCAT", SYS_PERMISSION.ANCESTORS))
                    .from(SYS_PERMISSION)
                    .where(
                        //SYS_PERMISSION.STATUS.eq(status).when(StrValidator.isNotEmpty(status))
                        //.and(
                        SYS_PERMISSION.TYPE.eq(type).when(StrValidator.isNotEmpty(type))
                        //)
                        .and(SYS_PERMISSION.NAME.like(word).when(StrValidator.isNotEmpty(word))
                            .or(SYS_PERMISSION.VALUE.like(word).when(StrValidator.isNotEmpty(word))))
                    )).when(StrValidator.isNotEmpty(word) || StrValidator.isNotEmpty(type))
                )
            ).orderBy(new QueryOrderBy(new QueryColumn(SYS_PERMISSION.SORT.getName()), "asc"));

        return query;
    }

    /**
     * <pre>
     * SELECT p.*
     * FROM sys_permission AS p
     *     LEFT JOIN sys_role_permission_mapping AS pm ON pm.permission_id = p.id
     * WHERE pm.role_id = #para(roleId)
     * </pre>
     */
    private QueryWrapper buildQueryByRole(SysRole role) {
        QueryWrapper query = QueryWrapper.create()
            .leftJoin(SYS_ROLE_PERMISSION_MAPPING).on(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(SYS_PERMISSION.ID))
            .where(SYS_ROLE_PERMISSION_MAPPING.ROLE_ID.eq(role.getId()));
        return query;
    }

    /**
     * <pre>
     * SELECT p.* 
     * FROM sys_permission AS p
     *     LEFT JOIN sys_role_permission_mapping AS rp ON rp.permission_id = p.id
     *     LEFT JOIN sys_user_role_mapping AS ur ON ur.role_id = rp.role_id 
     * WHERE #if(status)p.status = #para(status)#else p.status <> 'ON'#end
     *     #if(type)AND p.`type`=#para(type)#end
     *     #if(userId && userId > 1)AND ur.user_id = #para(userId)#end
     * </pre>
     */
    private QueryWrapper buildQueryByUser(SysUser user) {
        QueryWrapper query = QueryWrapper.create()
            .leftJoin(SYS_ROLE_PERMISSION_MAPPING).on(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(SYS_PERMISSION.ID))
            .leftJoin(SYS_USER_ROLE_MAPPING).on(SYS_USER_ROLE_MAPPING.ROLE_ID.eq(SYS_ROLE_PERMISSION_MAPPING.ROLE_ID))
            .where(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name())
                .and(SYS_USER_ROLE_MAPPING.USER_ID.eq(user.getId()).when(ObjUtil.isNotNull(user)))
            );
        return query;
    }

    @Override
    public boolean checkUnique(SysPermission bean) {
        // 查找已存在记录
        QueryWrapper query = QueryWrapper.create()
            .where(SYS_PERMISSION.NAME.eq(bean.getName())
                .and(SYS_PERMISSION.PARENT_ID.eq(bean.getParentId()))
                .and(SYS_PERMISSION.ID.ne(bean.getId()))
            );

        return ObjUtil.isNull(this.getOne(query));
    }

    @Override
    public List<SysPermission> listByMap(Map<String, Object> map) {
        return this.list(this.buildQuery(map));
    }

    @Cache(tags = CacheConstants.SYS_ROLE_TAG, key = "${loginId}_${loginType}",
        seconds = CacheConstants.CACHE_SECONDS_ONE_DAY)
    @Override
    public List<SysPermission> listByRole(SysRole role) {
        if (ObjUtil.isNull(role)) {
            return ListUtil.empty();
        }

        if (role.getId() == 1) {
            return this.list(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name()));
        }

        return this.list(this.buildQueryByRole(role));
    }

    @Cache(tags = CacheConstants.SYS_PERMISSION_TAG, key = "${loginId}_${loginType}",
        seconds = CacheConstants.CACHE_SECONDS_ONE_DAY)
    @Override
    public List<SysPermission> listByUser(SysUser user) {
        if (ObjUtil.isNull(user)) {
            return ListUtil.empty();
        }

        if (user.getId() == 1) {
            return this.list(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name()));
        }

        return this.list(this.buildQueryByUser(user));
    }

    @Override
    public boolean removeById(Serializable id) {
        if (this.getMapper().selectCountByCondition(SYS_PERMISSION.PARENT_ID.eq(id)) > 0) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "存在子项，不允许删除！");
        }
        
        if (this.rolePermissionMappingMapper.selectCountByCondition(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(id)) > 0) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "已分配于角色，不允许删除！");
        }
        
        
        return this.getMapper().deleteById(id) > 0;
    }

    @Override
    public boolean save(SysPermission bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限名称已经存在，请更换其它值！");
        }

        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        SysPermission partner = this.getById(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        return super.save(bean);
    }

    @Override
    public boolean update(SysPermission bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限名称已经存在，请更换其它值！");
        }

        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        SysPermission partner = this.getById(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        return this.updateById(bean);
    }

}
